package com.gift.recommendation.modules.admin.biz;

import com.gift.recommendation.Base.PageData;
import com.gift.recommendation.modules.login.dto.UserEntityExt;
import com.gift.recommendation.modules.login.service.LoginService;
import com.gift.recommendation.modules.user.dao.entity.ShoppingCartEntity;
import com.gift.recommendation.modules.user.service.ShoppingCartService;
import com.gift.recommendation.util.FileUpLoadUtils;
import com.gift.recommendation.util.MapSortUtils;
import com.gift.recommendation.util.PageTransUtil;
import com.gift.recommendation.modules.admin.dao.entity.FestivalRelationEntity;
import com.gift.recommendation.modules.admin.dao.entity.GoodsEntity;
import com.gift.recommendation.modules.admin.dto.*;
import com.gift.recommendation.modules.admin.service.FestivalRelationService;
import com.gift.recommendation.modules.admin.service.GoodsService;
import com.github.pagehelper.PageHelper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author zzh <2658137258@qq.com>
 * @date 2023/2/2
 */
@Service
public class GoodsImpl {
    @Autowired
    private GoodsService goodsService;

    @Autowired
    private FestivalRelationService festivalRelationService;

    @Autowired
    private LoginService loginService;

    @Autowired
    private FileUpLoadUtils fileUpLoadUtils;

    @Autowired
    private ShoppingCartService shoppingCartService;

    /**
     * 添加商品
     * @param req
     */
    public void addGoods(GoodsReq req) {
        judgePrice(req);
        GoodsEntity goods = new GoodsEntity();
        BeanUtils.copyProperties(req,goods);
        goods.setReserveStock(req.getStock());
        goods.setUuid(UUID.randomUUID().toString());
        goods.setIntroduceHtml(StringEscapeUtils.unescapeHtml4(req.getIntroduceHtml()));
        goods.setRotograph(1);
        goodsService.insertSelective(goods);

        if (CollectionUtils.isNotEmpty(req.getFestivalRelation())) {
            req.getFestivalRelation().stream().forEach(s -> {
                FestivalRelationEntity festivalRelationEntity = new FestivalRelationEntity();
                festivalRelationEntity.setFestivalUuid(s);
                festivalRelationEntity.setGoodsUuid(goods.getUuid());
                festivalRelationService.insertSelective(festivalRelationEntity);
            });
        }
    }

    /**
     * 修改商品
     * @param req
     */
    public void updateGoods(GoodsReq req) {
        judgePrice(req);
        Example example = new Example(FestivalRelationEntity.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("goodsUuid",req.getUuid());
        festivalRelationService.deleteByExample(example);
        if (CollectionUtils.isNotEmpty(req.getFestivalRelation())) {
            req.getFestivalRelation().stream().forEach(s -> {
                FestivalRelationEntity festivalRelationEntity = new FestivalRelationEntity();
                festivalRelationEntity.setFestivalUuid(s);
                festivalRelationEntity.setGoodsUuid(req.getUuid());
                festivalRelationService.insertSelective(festivalRelationEntity);
            });
        }
        GoodsEntity goods = new GoodsEntity();
        BeanUtils.copyProperties(req,goods);
        goods.setId(null);
        goods.setIntroduceHtml(StringEscapeUtils.unescapeHtml4(req.getIntroduceHtml()));
        Example exampleGoods = new Example(GoodsEntity.class);
        Example.Criteria criteriaGoods = exampleGoods.createCriteria();
        criteriaGoods.andEqualTo("uuid",req.getUuid());
        GoodsEntity goodsEntity = goodsService.selectByExample(exampleGoods).get(0);
        int reserve = goodsEntity.getReserveStock();
        if (goodsEntity.getReserveStock() <= req.getStock()) {
            reserve = goodsEntity.getReserveStock() - (goodsEntity.getStock() - req.getStock());
        } else {
            throw new RuntimeException("预留库存不允许为负数");
        }
        goods.setReserveStock(reserve);
        goodsService.updateByExampleSelective(goods,exampleGoods);
    }

    /**
     * 判断折扣单价是否有问题
     * @param req
     */
    public void judgePrice(GoodsReq req){
        BigDecimal price = req.getPrice();
        BigDecimal discountRate = req.getDiscountRate();
        BigDecimal discountPrice = req.getDiscountPrice();
        BigDecimal realPrice = price;
        if (discountPrice != null){
            realPrice = realPrice.subtract(discountPrice);
        }
        if (discountRate != null){
            realPrice = realPrice.multiply(discountRate);
        }
        realPrice.setScale(2, RoundingMode.HALF_UP);
        if (realPrice.doubleValue() <= 0){
            throw new RuntimeException("商品打折后单价小于等于0元,请重新输入");
        }
    }

    /**
     * 根据主键id获取商品
     * @param id
     * @return
     */
    public GoodsRsp goodsSearchById(Long id) {
        GoodsRsp goodsRsp = new GoodsRsp();
        GoodsEntity goods = goodsService.selectByPrimaryKey(id);
        BeanUtils.copyProperties(goods,goodsRsp);
        goodsRsp.setIntroduceHtml(StringEscapeUtils.escapeHtml4(goods.getIntroduceHtml()));
        Example exampleFestival = new Example(FestivalRelationEntity.class);
        Example.Criteria criteriaFestival = exampleFestival.createCriteria();
        criteriaFestival.andEqualTo("goodsUuid",goods.getUuid());
        List<String> festivalUuids = festivalRelationService.selectByExample(exampleFestival).stream().map(s -> {
            return s.getFestivalUuid();
        }).collect(Collectors.toList());
        goodsRsp.setRealPrice(goods.getDiscountRate().multiply((goods.getPrice().subtract(goods.getDiscountPrice()))));
        goodsRsp.setFestivalUuids(festivalUuids);
        return goodsRsp;
    }

    /**
     * 根据主键id删除商品
     * @param id
     */
    public void goodsDelete(Long id) {
        GoodsEntity goods = goodsService.selectByPrimaryKey(id);
        PictureReq pictureReq = new PictureReq();
        List<String> tmp = new ArrayList<>();
        tmp.add(goods.getPictureName());
        pictureReq.setPictureName(tmp);
        fileUpLoadUtils.deleteImg(pictureReq);
        Example exampleFestival = new Example(FestivalRelationEntity.class);
        Example.Criteria criteriaFestival = exampleFestival.createCriteria();
        criteriaFestival.andEqualTo("goodsUuid",goods.getUuid());
        if (CollectionUtils.isNotEmpty(festivalRelationService.selectByExample(exampleFestival))) {
            festivalRelationService.deleteByExample(exampleFestival);
        }
        Example exampleShoppingCart = new Example(ShoppingCartEntity.class);
        Example.Criteria criteriaShoppingCart = exampleShoppingCart.createCriteria();
        criteriaShoppingCart.andEqualTo("goodsUuid",goods.getUuid());
        shoppingCartService.deleteByExample(exampleShoppingCart);
        goodsService.deleteByPrimaryKey(id);
    }

    /**
     * 根据条件查询商品
     * @param req
     * @return
     */
    public PageData<GoodsEntityRsp> goodsList(GoodsListReq req) {
        com.github.pagehelper.Page<GoodsEntityRsp> page = PageHelper.startPage(
                req.getCurrentPage(), req.getPageSize()
        );
        if(req.getHighPrice() != null && req.getLowPrice() != null) {
            if (req.getLowPrice() > req.getHighPrice()) {
                throw new RuntimeException("输入有误");
            }
        }
        goodsService.selectList(req);
        if (CollectionUtils.isNotEmpty(page.getResult())) {
            page.getResult().stream().forEach(s ->{
                if(StringUtils.isNotEmpty(s.getFestivalName())) {
                    s.setFestivalNameList(Arrays.asList(s.getFestivalName().split(",")));
                }
            });
        }
        return PageTransUtil.createPageList(page, GoodsEntityRsp.class);
    }

    /**
     * 给出当前用户的猜你喜欢(协同过滤)
     * @param req
     * @return
     */
    public List<GoodsEntityRsp> guessLikeNoPoint(GuessLikeReq req) {
        //获取购买过此商品的用户的集合 最多1000条
        List<UserEntityExt> tmp = loginService.selectUserByGoods(req);
        Map<String,List<UserEntityExt>> userGoods = tmp.stream().collect(Collectors.groupingBy(UserEntityExt::getUserUuid));
        Long[][] sparseMatrix = new Long[userGoods.size()][userGoods.size()]; //用户稀疏矩阵，计算用户相似度
        //初始化用户稀疏矩阵
        for (int i = 0; i < userGoods.size();i++){
            for (int j = 0;j < userGoods.size();j++){
                sparseMatrix[i][j] = (long)0;
            }
        }
        Map<String,Integer> userItemLength = new HashMap<>(); //每个用户买了多少件商品
        Map<String, Set<String>> itemUserCollection = new HashMap<>(); //用户->商品 倒排表
        Set<String> items = new HashSet<>(); //存储商品集合
        Map<Integer,String> idUser = new HashMap<>();
        Map<String,Integer> userID = new HashMap<>(); //将矩阵的i j和用户一一对应
        AtomicReference<Integer> i = new AtomicReference<>(0);
        userGoods.forEach((key,value) ->{
            userItemLength.put(key,value.size());
            idUser.put(i.get(),key);
            userID.put(key,i.get()); //将矩阵的i j和用户一一对应
            i.set(i.get() + 1);
            List<String> goods = value.stream().map(UserEntityExt::getGoodsUuid).collect(Collectors.toList());
            for (int j = 0;j < goods.size();j++){
                String goodsUuid = goods.get(j);

                //建立商品-用户倒排表
                if (items.contains(goodsUuid)){
                    itemUserCollection.get(goodsUuid).add(key);
                }else{
                    items.add(goodsUuid);
                    itemUserCollection.put(goodsUuid,new HashSet<>());
                    itemUserCollection.get(goodsUuid).add(key);
                }
            }
        });

        //计算相似度矩阵【稀疏】
        Set<Map.Entry<String,Set<String>>> entrySet = itemUserCollection.entrySet();
        Iterator<Map.Entry<String,Set<String>>> iterator = entrySet.iterator();
        while (iterator.hasNext()){
            Set<String> commonUsers = iterator.next().getValue();
            for(String userU:commonUsers){
                for (String userV:commonUsers){
                    if (userU.equals(userV)){
                        continue;
                    }
                    //计算用户u和用户v都有正反馈的物品总数
                    sparseMatrix[userID.get(userU)][userID.get(userV)]+=1;
                }
            }
        }

        //计算这个用户的物品推荐度
        Map<String,Double> itemRecommendDegree = new HashMap<>(); //物品uuid->推荐度
        for (String item : items){
            //购买当前物品的所有用户集合
            Set<String> users = itemUserCollection.get(item);
            //如果被推荐用户没有购买当前物品，则进行推荐度计算
            if (!users.contains(req.getUuid())){
                double recommendDegree = 0.0;
                for (String user : users){
                    //计算用户之间的相似度【余弦相似性】 暂未计算权重
                    recommendDegree += sparseMatrix[userID.get(req.getUuid())][userID.get(user)]/
                            Math.sqrt(userItemLength.get(req.getUuid())*userItemLength.get(user));
                }
                itemRecommendDegree.put(item,recommendDegree);
            }
        }
        List<String> list= MapSortUtils.sortMapByValues(itemRecommendDegree).keySet().stream().collect(Collectors.toList());
        //只取出前6条
        if (list.size() > 6){
            list = list.subList(0,6);
        }
        Example example = new Example(GoodsEntity.class);
        Example.Criteria criteria = example.createCriteria();
        List<GoodsEntityRsp> result = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)) {
            criteria.andIn("uuid",list);
            StringBuffer orderByString = new StringBuffer();
            orderByString.append("FIELD(uuid");
            list.forEach(s->{
                orderByString.append(",'" + s + "'");
            });
            orderByString.append(")");
            example.setOrderByClause(orderByString.toString());
            result = goodsService.selectByExample(example).stream().map(s ->{
                GoodsEntityRsp goodsEntityRsp = new GoodsEntityRsp();
                BeanUtils.copyProperties(s,goodsEntityRsp);
                goodsEntityRsp.setRealPrice(s.getDiscountRate().multiply(s.getPrice().subtract(s.getDiscountPrice())));
                return goodsEntityRsp;
            }).collect(Collectors.toList());
        }
        return result;
    }

    /**
     * 商品信息统计
     * @param req
     * @return
     */
    public PageData<GoodsSaleStatusRsp> goodsSaleStatus(GoodsSaleStatusReq req) {
        com.github.pagehelper.Page<GoodsEntityRsp> page = PageHelper.startPage(
                req.getCurrentPage(), req.getPageSize()
        );
        goodsService.goodsSaleStatus(req);
        return PageTransUtil.createPageList(page, GoodsSaleStatusRsp.class);
    }

    /**
     * 商品入库
     * @param req
     */
    public void storage(GoodsReq req) {
        Example example = new Example(GoodsEntity.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("uuid",req.getUuid());
        GoodsEntity goods = goodsService.selectByExample(example).get(0);
        Integer result = goods.getStock() + req.getStock();
        Integer reserve = goods.getReserveStock() + req.getStock();
        GoodsEntity tmp = new GoodsEntity();
        tmp.setStock(result);
        tmp.setReserveStock(reserve);
        goodsService.updateByExampleSelective(tmp,example);
    }

    /**
     * 设置轮播图
     * @param req
     */
    public void addRotograph(GoodsReq req) {
        judgeRotograph();
        Example exampleGoods = new Example(GoodsEntity.class);
        Example.Criteria criteriaGoods = exampleGoods.createCriteria();
        criteriaGoods.andEqualTo("uuid",req.getUuid());
        GoodsEntity goods = new GoodsEntity();
        goods.setRotograph(2);
        goodsService.updateByExampleSelective(goods,exampleGoods);
    }

    /**
     * 判断轮播图数量
     */
    public void judgeRotograph() {
        Example exampleGoods = new Example(GoodsEntity.class);
        Example.Criteria criteriaGoods = exampleGoods.createCriteria();
        criteriaGoods.andEqualTo("rotograph",2);
        if (goodsService.selectByExample(exampleGoods).size() >= 10) {
            throw new RuntimeException("轮播图最多只允许设置10个商品");
        }
    }

    /**
     * 取消轮播图
     * @param req
     */
    public void deleteRotograph(GoodsReq req) {
        Example exampleGoods = new Example(GoodsEntity.class);
        Example.Criteria criteriaGoods = exampleGoods.createCriteria();
        criteriaGoods.andEqualTo("uuid",req.getUuid());
        GoodsEntity goods = new GoodsEntity();
        goods.setRotograph(1);
        goodsService.updateByExampleSelective(goods,exampleGoods);
    }
}
